<?php
class wzvm{
	var $MAXINS=999;
	var $debug=false;
	var $int=Array(10);
	var $W_memory=Array(10000);
	var $W_top=0;
	var $recursion=Array();
	var $ex=Array(0,0,0,0,0,0,0,0,0,0);
	var $sex=Array(0,0,0,0,0,0,0,0,0,0);
	
	function wzvm(){
		$this->recursion[]=1000;
	}
	
	function execline($opcode){
	/*
		This function will execute a single instruction of $opcode
	*/
		$opcode=abs((int)$opcode)%1000;
		if ($opcode==0){
			//ext
			$this->ex[0]=10000;
			return;
		}
		if ($opcode==1){
			//ret
			$this->ex[0]=array_pop($this->recursion);
			return;
		}
		if ($opcode<10){
			//int
			if($this->int[$opcode]){
				$x=$this->int[$opcode];
				@$x();
			}
			return;
		}
		if($opcode==10){
			for($i=0;$i<100;$i++);//rudimentary sleep
			return;
		}
		if ($opcode<100){
			$r=$this->ex[$opcode%10];
			if($opcode%10==0)return;
			switch((int)($opcode/10)){
				case 1:
					//jmp
					$r=abs($r);
					$this->ex[0]=$r;
				break;
				case 2:
					//call
					$r=abs($r);
					array_push($this->recursion,$this->ex[0]+1);
					$this->ex[0]=$r;
				break;
				case 3:
					//push
					$rr=(int)$this->W_top;
					$this->W_memory[$rr]=$r;
					$this->W_top++;
				break;
				case 4:
					//pop
					$this->ex[$opcode%10]=(int)($this->W_memory[-1+$this->W_top--]);
				break;
				case 5:
					//inc
					$this->ex[$opcode%10]=($r+1)%10000;
				break;
				case 6:
					//dec
					$this->ex[$opcode%10]=($r-1)%10000;
				break;
				case 7:
					//clr
					$this->ex[$opcode%10]=0;
				break;
				case 8:
					//save
					$this->sex[$opcode%10]=$r*1;
				break;
				case 9:
					//load
					$this->ex[$opcode%10]=$this->sex[$opcode%10]*1;
				break;
			}
			return;
		}
		$r1=(int)(($opcode%100)/10);
		$r2=$opcode%10;
		if ((int)(($opcode%100)/10)==0 && $opcode<80)return;
		switch((int)($opcode/100)){
			case 1:
				//mov eNx CONST
				$this->ex[$r1]=$r2;
			break;
			case 2:
				//sum eNx CONST
				$this->ex[$r1]=($this->ex[$r1]+$r2)%10000;
			break;
			case 3:
				//mul eNx CONST
				$this->ex[$r1]=($this->ex[$r1]*$r2)%10000;
			break;
			case 4:
				//mov eNx eMx
				$this->ex[$r1]=$this->ex[$r2];
			break;
			case 5:
				//sum eNx eMx
				$this->ex[$r1]=($this->ex[$r1]+$this->ex[$r2])%10000;
			break;
			case 6:
				//res eNx eMx
				$this->ex[$r1]=($this->ex[$r1]-$this->ex[$r2])%10000;
			break;
			case 7:
				//mov eNx [eMx]
				$this->ex[$r1]=$this->W_memory[abs($this->ex[$r2])];
			break;
			case 8:
				//mov [eNx] eMx
				$this->W_memory[abs($this->ex[$r1])]=$this->ex[$r2];
			break;
			case 9:
				//jnz eNx eMx
				if($this->ex[$r1]){
					$this->ex[0]=abs($this->ex[$r2]);
				}
			break;
		}
	}
	function evalOP($vim){
		$this->ex[0]=0;
		$c=0;
		while($this->ex[0]<10000 && $vim[$this->ex[0]] && $c<$this->MAXINS){
			$c++;
			if($this->debug){
				echo "{$this->ex[0]} - ".$vim[$this->ex[0]]." -- ";
			}
			$prev0=$this->ex[0];
			$this->execline($vim[$this->ex[0]]);
			if($this->debug){
				echo implode(",",$this->ex)."\n";
			}
			if($prev0==$this->ex[0]){
				$this->ex[0]++;
			}
		}
	}
}
?>